define([
    'js/resolve',
    'angular-cookies',
    'angular-toastr.tpls',
    'ace-language-tools',
    'js/components/util',
    'js/components/dialog',
    'js/components/prettier'
], function(resolve) {
    angular.module('app.aceeditor', [
            'ngCookies',
            'toastr',
            'app.util',
            'app.dialog',
            'app.prettier'
        ])
        .directive('aceeditor', aceEditorDirective)
        .run(function($templateCache) {
            $templateCache.put('templates/extendbar/editor.html',
                '<div class="btn-group-vertical">' +
                '  <button type="button" class="btn btn-default" title="单行注释"' +
                '      ng-click="extendbar.comment.click(\'line\')">&nbsp;//&nbsp;</button>' +
                '  <button type="button" class="btn btn-default" title="多行注释"' +
                '      ng-click="extendbar.comment.click(\'block\')">/**/</button>' +
                '  <button type="button" class="btn btn-default" title="折叠"' +
                '      ng-click="extendbar.fold.click()"><i class="fa fa-minus-square-o"></i></button>' +
                '  <button type="button" class="btn btn-default" title="展开"' +
                '      ng-click="extendbar.unfold.click()"><i class="fa fa-plus-square-o"></i></button>' +
                '  <button type="button" class="btn btn-default" title="格式化"' +
                '      ng-click="extendbar.format.click()"><i class="fa fa-code"></i></button>' +
                '  <button type="button" class="btn btn-default" title="行号跳转"' +
                '      ng-click="extendbar.goto.click()"><i class="fa fa-list-ol"></i></button>' +
                '</div>');
        });

    function aceEditorDirective($rootScope, $timeout, $compile, $cookies, $templateCache, toastr, _util,
            _dialog, _prettier) {
        const defaultOptions = {
            //mode: modeId,
            theme: 'ace/theme/chrome',
            cursorStyle: 'slim',
            fontSize: 14,
            wrap: true,
            useWorker: false,
            showLineNumbers: true,
            showInvisibles: false,
            printMarginColumn: 120,
            showPrintMargin: false,
            autoScrollEditorIntoView: true,
            enableSnippets: true,
            enableLiveAutocompletion: true,
            enableBasicAutocompletion: true
        };
        const fileAttributes = {
            '.java': { mode: 'java', formatter: '' },
            '.cpp': { mode: 'c_cpp', formatter: '' },
            '.cs': { mode: 'csharp', formatter: '' },
            '.py': { mode: 'python', formatter: '' },
            '.go': { mode: 'golang', formatter: '' },
            '.groovy': { mode: 'groovy', formatter: '' },
            '.swift': { mode: 'swift', formatter: '' },
            '.html': { mode: 'html', formatter: {
                parser: 'html'
            }},
            '.css': { mode: 'css', formatter: {
                parser: 'css'
            }},
            '.js': { mode: 'javascript', formatter: {
                parser: 'typescript'
            }},
            '.md': { mode: 'markdown', formatter: {
                parser: 'markdown'
            }},
            '.sql': { mode: 'mysql', formatter: {
                parser: 'sql',
                tabWidth: 4
            }},
            '.sh': { mode: 'sh', formatter: '' },
            '.xml': { mode: 'xml', formatter: {
                parser: 'xml',
                tabWidth: 4
            }},
            '.yml': { mode: 'yaml', formatter: {
                parser: 'yaml'
            }},
            '.json': { mode: 'json5', formatter: {
                parser: 'json5',
                tabWidth: 4
            }},
            '.txt': { mode: 'text', formatter: '' }
        };
        //
        return resolve({
            restrict: 'E',
            replace: true,
            templateUrl: 'js/templates/aceeditor.html',
            scope: {
                file: '=',
                context: '=',
                extend: '<',
                renderer: '<',
                onChanged: '<'
            },
            compile: function() {
                var toolscope = $rootScope.$new(true);
                toolscope.extendbar = {
                    'comment': { click: null },
                    'fold': { click: null },
                    'unfold': { click: null },
                    'format': { click: null },
                    'goto': { click: null }
                };
                var template = $templateCache.get('templates/extendbar/editor.html');
                var extendbar = $($compile(template)(toolscope));
                extendbar.css('display', 'none');
                extendbar.prependTo($('.extendbar'));

                return {
                    post: function(scope, element) {
                        var context = { init: true, reload: false };
                        // editor
                        var fileAttrs = fileAttributes[scope.file.extension] || {};
                        var options = angular.copy(defaultOptions);
                        angular.extend(options, $cookies.getObject('editor.options') || {});
                        options['mode'] = 'ace/mode/' + (fileAttrs['mode'] || 'plain_text');
                        options['showGutter'] = options['showLineNumbers'];
                        var editor = ace.edit(element.find('>pre')[0], options);
                        // init value
                        editor.setValue('', 1);
                        // set options
                        scope.$on('@editor.option', function(event, option) {
                            if (option) {
                                _.each(option, function(attrValue, attrName) {
                                    editor.setOption(attrName, attrValue);
                                    if (attrName == 'showLineNumbers') {
                                        editor.setOption('showGutter', attrValue);
                                    }
                                });
                            }
                        });

                        scope.editor = {
                            'undo': scope.context.navbar['edit']['undo'],
                            'redo': scope.context.navbar['edit']['redo'],
                            'cut': scope.context.navbar['edit']['cut'],
                            'copy': scope.context.navbar['edit']['copy'],
                            'paste': scope.context.navbar['edit']['paste'],
                            'find': scope.context.navbar['edit']['find'],
                            'source': {
                                'enabled': true,
                                'comment': toolscope.extendbar['comment'],
                                'fold': toolscope.extendbar['fold'],
                                'unfold': toolscope.extendbar['unfold'],
                                'format': toolscope.extendbar['format'],
                                'goto': toolscope.extendbar['goto']
                            },
                            'save': scope.context.navbar['file']['save'],
                            'statuses': ['可写', '插入', '1 : 1']
                        };

                        // init extend menu
                        if (scope.extend) {
                            var template = scope.extend.template
                            if (!template) {
                                template = $templateCache.get(scope.extend.templateUrl);
                            }
                            $($compile(template)(scope)).replaceAll(element.find('extendmenu'));
                            scope.extend.renderer.call(scope, editor);
                        } else {
                            scope.extend = 'default';
                            element.find('extendmenu').remove();
                        }

                        // event listeners
                        var undoManager = editor.session.getUndoManager();
                        editor.on('change', function(delta) {
                            $timeout(function() {
                                if (context.init) {
                                    undoManager.reset();
                                }
                                if (!context.init && !context.reload) {
                                    scope.file.changed = true;
                                    scope.file.data = editor.getValue();
                                }

                                scope.editor['save'].enabled = scope.file.changed;
                                scope.editor['undo'].enabled = undoManager.canUndo();
                                scope.editor['redo'].enabled = undoManager.canRedo();

                                if (scope.file.changed || context.init) {
                                    $timeout(function() {
                                        if (scope.onChanged && typeof(scope.onChanged) === 'function') {
                                            scope.onChanged(editor.getValue());
                                        }
                                    });
                                }
                            });
                        });
                        editor.on('changeSelection', function() {
                            $timeout(function() {
                                var hasSelected = (editor.getSelectedText() != '');
                                scope.editor['cut'].enabled = scope.editor['copy'].enabled = hasSelected;
                            });
                        });

                        // update status
                        function onStatusUpdated() {
                            $timeout(function() {
                                var lead = editor.selection.lead;
                                scope.editor.statuses[1] = editor.session.getOverwrite() ? '覆盖' : '插入';
                                scope.editor.statuses[2] = (lead.row + 1) + ' : ' + (lead.column + 1);
                                scope.context.statusbar.statuses = scope.editor.statuses;
                            });
                        }
                        editor.on('changeSelection', onStatusUpdated);
                        editor.on('keyboardActivity', onStatusUpdated);

                        // add hotkeys
                        editor.commands.addCommand({
                            name: 'format',
                            bindKey: { win: 'Ctrl-Shift-F', mac: 'Command-Shift-S' },
                            exec: function() {
                                scope.editor['source']['format'].click();
                            }
                        });
                        editor.commands.removeCommand('replace');
                        editor.commands.addCommand({
                            name: 'findAndReplace',
                            bindKey: { win: 'Ctrl-F', mac: 'Command-F' },
                            exec: function() {
                                _dialog.open().showSearchbox({ editor: editor });
                            }
                        });
                        editor.commands.addCommand({
                            name: 'gotoLine',
                            bindKey: { win: 'Ctrl-L', mac: 'Command-L' },
                            exec: function() {
                                scope.editor['source']['goto'].click();
                            }
                        });
                        editor.commands.addCommand({
                            name: 'saveFile',
                            bindKey: { win: 'Ctrl-S', mac: 'Command-S' },
                            exec: function() {
                                if (scope.file.changed) {
                                    scope.editor['save'].click();
                                }
                            }
                        });

                        scope.file.reloader = function(helper, reloaded) {
                            context.reload = true;
                            if (reloaded) {
                                loadData(scope.file.data);
                            } else if (scope.file.path) {
                                helper.read(scope.file).then(function(fileObj) {
                                    loadData(fileObj.data);
                                });
                            } else if (scope.file.data) {
                                loadData(scope.file.data);
                            } else {
                                context.init = false;
                                context.reload = false;
                            }

                            function loadData(data) {
                                editor.setValue(data, -1);
                                editor.resize();
                                $timeout(function() {
                                    context.init = false;
                                    context.reload = false;
                                    scope.file.changed = false;
                                    scope.editor['save'].enabled = false;
                                }, 50);
                            }
                        }

                        scope.file.extractor = function() {
                            scope.file.data = editor.getValue();
                        }

                        scope.file.renderer = function(helper) {
                            // extendbar
                            extendbar.css('display', '');

                            // editor
                            editor.focus();
                            editor.resize();
                            if (!scope.file.opened) {
                                scope.file.opened = true;
                                scope.file.reloader(helper);
                            }

                            var codeFormatter = function() {
                                if (fileAttrs['formatter']) {
                                    try {
                                        var code = _prettier.format(editor.getValue(), fileAttrs['formatter']);
                                        editor.setValue(code, -1);
                                        editor.focus();
                                        editor.resize();
                                        toastr.success('文件内容已格式化！');
                                    } catch (e) {
                                        toastr.error('文件内容格式不正确！');
                                        console.log(e);
                                    }
                                } else {
                                    toastr.warning('不支持格式化此文件！');
                                }
                            };

                            // extendbar
                            toolscope.extendbar['comment'].click = function(type) {
                                editor.focus();
                                if (type == 'block') {
                                    editor.toggleBlockComment();
                                } else {
                                    editor.toggleCommentLines();
                                }
                            };
                            toolscope.extendbar['fold'].click = function() {
                                editor.focus();
                                editor.session.toggleFold(false);
                            };
                            toolscope.extendbar['unfold'].click = function() {
                                editor.focus();
                                editor.session.toggleFold(true);
                            };
                            toolscope.extendbar['format'].click = codeFormatter;
                            toolscope.extendbar['goto'].click = function() {
                                _dialog.open().gotoLine({
                                    maxLine: editor.session.getLength()
                                }).then(function(line) {
                                    editor.focus();
                                    editor.gotoLine(line);
                                });
                                editor.focus();
                            };
                            scope.context.navbar['file']['save'].enabled = scope.file.changed;
                            scope.context.navbar['file']['save'].click = function() {
                                helper.save(scope.file);
                            };

                            // navbar
                            scope.context.navbar['edit']['undo'].enabled = undoManager.canUndo();
                            scope.context.navbar['edit']['undo'].click = function() {
                                editor.focus();
                                editor.undo();
                            };
                            scope.context.navbar['edit']['redo'].enabled = undoManager.canRedo();
                            scope.context.navbar['edit']['redo'].click = function() {
                                editor.focus();
                                editor.redo();
                            };
                            var hasSelected = !_.isEmpty(editor.getSelectedText());
                            scope.context.navbar['edit']['cut'].enabled = hasSelected;
                            scope.context.navbar['edit']['cut'].click = function() {
                                editor.focus();
                                _util.copy(editor.getSelectedText());
                                editor.commands.exec('cut', editor);
                            };
                            scope.context.navbar['edit']['copy'].enabled = hasSelected;
                            scope.context.navbar['edit']['copy'].click = function() {
                                editor.focus();
                                _util.copy(editor.getSelectedText());
                                editor.commands.exec('copy', editor);
                            };
                            scope.context.navbar['edit']['paste'].enabled = false;
                            scope.context.navbar['edit']['paste'].click = function(event) {
                                editor.focus();
                                // TODO
                            };
                            scope.context.navbar['edit']['selectAll'].enabled = true;
                            scope.context.navbar['edit']['selectAll'].click = function() {
                                editor.focus();
                                editor.selectAll();
                            }
                            scope.context.navbar['edit']['delete'].enabled = true;
                            scope.context.navbar['edit']['delete'].click = function() {
                                editor.focus();
                                editor.removeLines();
                            };
                            scope.context.navbar['edit']['find'].enabled = true;
                            scope.context.navbar['edit']['find'].click = function() {
                                _dialog.open().showSearchbox({
                                    editor: editor
                                });
                            };
                            scope.context.navbar['edit']['format'].enabled = true;
                            scope.context.navbar['edit']['format'].click = codeFormatter;

                            // statusbar
                            scope.context.statusbar.link = scope.file.path || scope.file.name;
                            scope.context.statusbar.statuses = scope.editor.statuses;

                            if (typeof(scope.renderer) === 'function') {
                                scope.renderer(helper, editor);
                            }
                        }

                        // resize
                        function handleResize() {
                            if (element.is(':visible')) {
                                $timeout(function() {
                                    editor.resize();
                                });
                            }
                        }
                        scope.$watch(function() {
                            return [element.width(), element.height()];
                        }, function() {
                            handleResize();
                        }, true);
                        scope.$on('@resize.editor', function(event) {
                            handleResize();
                        });
                        scope.$on('@resize.all', function(event) {
                            handleResize();
                        });

                        // destroy
                        scope.$on('$destroy', function() {
                            editor.destroy();
                            editor.container.remove();
                        });
                    }
                };
            }
        });
    }
});
